<?php

/**
 * Created by PhpStorm
 * User: 林振宇
 * Date: 2020/8/14
 * Time: 10:55
 */

namespace app\api\service;

use app\api\controller\Base;
use app\api\library\WeChat\MiniProgram;
use app\api\library\WeChat\Payment;
use app\api\model\Cancel as CancelModel;
use app\api\model\Order as OrderModel;
use app\api\model\Product as ProductModel;
use app\api\model\User as UserModel;
use app\common\controller\Messages;
use app\common\enum\OrderEnum;
use app\common\enum\ProductEnum;
use app\common\enum\TemplateEnum;
use think\Db;
use think\Exception;
use think\Log;
use think\Request;

class OrderService extends Base
{

    /**
     * 购票
     * @param $userId
     * @param $openId
     * @return array
     * @since: 2020/8/21
     * @author: Lin Zhengyu
     */
    public function tickets($userId, $openId)
    {
        $order_number = self::makeOrderNo();
        $request = Request::instance()->post();
        parent::validate($request, 'OrderValidate');
        /** yunzhihui 新增预约时间校验 **/
        $is_reserve = 1;
        $reserve_date = $request['reserve_date'];
        if(!isset($reserve_date) || empty($reserve_date)){
            $is_reserve = 0;
            $reserve_date = date("Y-m-d");
        }
        /** yunzhihui 新增预约时间校验END **/
        Db::startTrans();
        try {
            $product_snapshot = (new ProductModel())->where('id', '=', $request['product_id'])->find(); //商品查询
            if ($product_snapshot['specs_id'] == ProductEnum::SPECS_PEOPLE) { //如果当前规格按人数
                $res = $this->specsHead($product_snapshot, $request, $userId, $order_number, $openId, $is_reserve, $reserve_date); //按人数
            } else if ($product_snapshot['specs_id'] == ProductEnum::SPECS_SHIP) {//如果当前规格按船
                $res = $this->specsBoat($product_snapshot, $request, $userId, $order_number, $openId, $is_reserve, $reserve_date); //按船
            }
            Db::commit();
            return $res;
        } catch (Exception $ex) {
            Log::info(1633);
            Db::rollback();
            parent::error('error', $ex->getMessage());
            Log::info('[error] 创建本地订单失败 ' . date('Y-m-d H:i:s', time()) . '，错误原因：' . $ex->getMessage());
        }
    }

    /**
     * 按船规格的数据形式
     * @param $product_snapshot //商品快照
     * @param $request // 传入数据
     * @param $userId //用户ID
     * @param $order_number //订单号
     * @param $openId //openID
     * @return array
     * @author: Lin Zhengyu
     * @since: 2020/8/21
     */
    public function specsBoat($product_snapshot, $request, $userId, $order_number, $openId, $is_reserve, $reserve_date)
    {
        // 商品快照
        $product = [
            'name' => $product_snapshot['name'],//商品名称
            'vessel_type' => $request['vessel_type'],//商品类型
            'image' => $product_snapshot['image'], //商品图
            'number' => $product_snapshot['number'],//限载人数
        ];

        $data = [
            'order_number' => $order_number, //订单号
            'product_id' => $request['product_id'], //商品ID
            'user_id' => $userId, //用户ID
            'order_price' => $product_snapshot['deposit'], //订单金额
            'price' => $product_snapshot['price'], //最低消费金额
            'min_time' => $product_snapshot['min_time'], //最低消费时间
            'after_price' => $product_snapshot['after_price'], //超时后金额
            'after_min_time' => $product_snapshot['after_min_time'], //超时后时间
            'product_snapshot' => json_encode($product), //商品快照
            'specs_id' => OrderEnum::SPECS_BOAT, //按船
            'is_reserve' => $is_reserve,
            'reserve_date' => $reserve_date,
        ];
        OrderModel::create($data);
        return [
            'body' => '购买船票', // 商品描述（产品标题）
            'out_trade_no' => $order_number, // 订单号（创建订单生成的订单号）
            'total_fee' => $product_snapshot['deposit'] * 100, // 价格  单位：分，所以要 * 100
            'trade_type' => 'JSAPI', // 请对应换成你的支付方式对应的值类型
            'openid' => $openId, // trade_type=JSAPI，此参数必传，用户在商户appid下的唯一标识，
        ];

    }

    /**
     * 按人数规格的数据形式
     * @param $product_snapshot
     * @param $request
     * @param $userId
     * @param $order_number
     * @param $openId
     * @return array
     * @author: Lin Zhengyu
     * @since: 2020/8/21
     */
    public function specsHead($product_snapshot, $request, $userId, $order_number, $openId)
    {
        // 商品快照
        $product = [
            'name' => $product_snapshot['name'],//商品名称
            'vessel_type' => $request['vessel_type'],//商品类型
            'image' => $product_snapshot['image'], //商品图
            'number' => $product_snapshot['number'],//限载人数
        ];

        $order_price = $product_snapshot['price'] * $request['num_people']; //船票价格 * 船票数量

        $data = [
            'order_number' => $order_number, //订单号
            'product_id' => $request['product_id'], //商品ID
            'user_id' => $userId, //用户ID
            'num_people' => $request['num_people'], //船票数量
            'order_price' => $order_price, //订单金额
            'product_snapshot' => json_encode($product), //商品快照
            'specs_id' => OrderEnum::SPECS_HEAD, //按人数
        ];
        OrderModel::create($data);
        return [
            'body' => '购买船票', // 商品描述（产品标题）
            'out_trade_no' => $order_number, // 订单号（创建订单生成的订单号）
            'total_fee' => $order_price * 100, // 价格  单位：分，所以要 * 100
            'trade_type' => 'JSAPI', // 请对应换成你的支付方式对应的值类型
            'openid' => $openId, // trade_type=JSAPI，此参数必传，用户在商户appid下的唯一标识，
        ];
    }


    //支付成功后修改订单状态
    public function updateOrderStatus($orderId)
    {
        (new OrderModel())->save(array('status' => OrderEnum::ORDER_WRITTEN), array('id' => $orderId));
    }

    //生成核销编号并插入数据库
    public function updateCancelNumber($orderId, $productId)
    {
        (new OrderModel())->save(array('qr_number' => self::cancelNumber($productId)), array('id' => $orderId));
    }

    //生成核销二维码并插入数据库
    public function updateQrCode($data, $orderId)
    {
        (new OrderModel())->save(array('qr_code' => self::getCode($data)), array('id' => $orderId));
    }

    /**
     * 进场核销
     * @param $order
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @since: 2020/8/19
     * @author: Lin Zhengyu
     */
    public function approach($order)
    {
        /**
         * @Todo  1.先解密数据，解密成功 验证当前核销码是否存在，异常
         * @Todo  2.验证当前船票是否是待核销状态
         * @Todo  3.这个订单是否是当前订单用户
         *
         */
        if ($order['status'] != OrderEnum::ORDER_WRITTEN) {
            $this->error('当前船票已入场核销请勿重复核销');
        }
        $data = (new OrderModel())->save(['status' => OrderEnum::ORDER_SERVICE, 'admission_time' => time(), 'update_time' => time()], ['id' => $order['id']]); //将订单状态修改成已核销（使用中）
        $new_order = (new OrderModel())->where('id', '=', $order['id'])->find(); //更新后的订单
        $openId = (new UserModel())->where('id', '=', $new_order['user_id'])->value('openid'); //提醒的用户openid
        $tmp = (new Messages())->admission($new_order); //消息模板
        (new MiniProgram())->templateMessage(TemplateEnum::Tmp_ADMISSION, $openId, '/pages/subpage/orderTimeDetails/orderTimeDetails?order_id=' . $new_order['id'] . '&type=0', $tmp); //订阅消息
        $this->success('进场核销成功', $data);
    }

    /**
     * 出场核销
     * @param $order
     * @param $qr_code
     * @param $userId
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @author: Lin Zhengyu
     * @since: 2020/8/19
     */
    public function appear($order, $qr_code, $userId)
    {
        /**
         * @Todo  1.先解密数据，解密成功 验证当前核销码是否存在，异常
         * @Todo  2.验证当前船票是否是使用中状态
         * @Todo  3.这个订单是否是当前订单用户
         * @Todo  4.计算当前入场时间和出场时间时间差（出场时间-入场时间） = 时间差，将时间差转换成 分
         * @Todo  5.判断当前时间差是否超时，1.未超时则按照未超时的计费规则计算价格，2.超时则按照超时的计费规则
         */
        if ($order['status'] == OrderEnum::ORDER_AFTER) {
            $this->error('当前船票已出场核销请勿重复核销');
        }
        if ($order['status'] == OrderEnum::ORDER_SERVICE) { //如果当前状态是使用中
            (new OrderModel())->save(['status' => OrderEnum::ORDER_AFTER, 'appearance_time' => time(), 'update_time' => time()], ['id' => $order['id']]);
            $order2 = (new OrderModel())->where('qr_number', '=', $qr_code)->find(); //出场后更新的订单
            $diff = $this->difference($order2['admission_time'], $order2['appearance_time']); //游玩时间
            $openId = (new UserModel())->where('id', '=', $order2['user_id'])->value('openid'); //提醒的用户openid
            //未超时计费规则
            if ($diff <= $order2['min_time']) { //如果当前游玩时间未超出了最低消费时间，则按照最低消费时间计算
                $settlement = $order2['order_price'] - $order2['price']; //押金 - 最低消费金额
                $data = (new OrderModel())->save(['tk_price' => $settlement, 'status' => OrderEnum::ORDER_AFTER, 'diff_time' => $diff, 'update_time' => time()], ['id' => $order2['id']]); //更新当前剩余押金金额并修改状态为已核销

                $this->generateCancel($order2, $userId);  //核销记录

                $tmp = (new Messages())->appearance($order2); //消息模板
                (new MiniProgram())->templateMessage(TemplateEnum::Tmp_END, $openId, '/pages/subpage/orderTimeDetails/orderTimeDetails?order_id=' . $order2['id'] . '&type=1', $tmp); //订阅消息

                $res = (new Payment())->refund($order2['order_number'], $settlement, $order2['order_price'] * 100, $order2['tk_price'] * 100);//拉起退款
                $this->rel_code($res, $order2['id']); //修改订单状态为已退款
                $tmp = (new Messages())->refund2($order2); //消息模板
                (new MiniProgram())->templateMessage(TemplateEnum::Tmp_REFUND, $openId, '/', $tmp); //订阅消息


                $this->success('出场核销成功', $data);
            }

//@Todo--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

            //超时计费规则

            if ($diff > $order2['min_time']) { //如果当前游玩时间超出了最低消费时间
                $overtime = $diff - $order2['min_time']; //超出时间
                /**
                 * @Todo 1. 在已扣除最低消费金额的基础上 计算当前超时多久，再去对比
                 */
                $after_price = $order2['after_price']; //超时计费规则
                $after_min = ceil($overtime / $order2['after_min_time']) * $after_price; //超时金额
                $settlement = $order2['order_price'] - $order2['price'] - $after_min; //退还金额 (押金-最低消费-超时计费金额)
                if ($settlement < 0) {
                    $settlement = 0; //负数取0
                }
                $data = (new OrderModel())->save(['tk_price' => $settlement, 'status' => OrderEnum::ORDER_AFTER, 'diff_time' => $diff, 'over_time' => $overtime, 'update_time' => time()], ['id' => $order2['id']]); //更新当前剩余押金金额并修改状态为已核销

                $this->generateCancel($order2, $userId);//核销记录
                $tmp = (new Messages())->appearance($order2); //消息模板
                (new MiniProgram())->templateMessage(TemplateEnum::Tmp_END, $openId, '/pages/subpage/orderTimeDetails/orderTimeDetails?order_id=' . $order2['id'] . '&type= ', $tmp); //订阅消息

                $res = (new Payment())->refund($order2['order_number'], $settlement, $order2['order_price'] * 100, $order2['tk_price'] * 100);//拉起退款
                $this->rel_code($res, $order2['id']); //修改订单状态为已退款
                $tmp = (new Messages())->refund2($order2); //消息模板
                (new MiniProgram())->templateMessage(TemplateEnum::Tmp_REFUND, $openId, '/', $tmp); //订阅消息


                $this->success('出场核销成功', $data);

            }
        }

    }

    //退款成功回调
    public function rel_code($data, $orderId)
    {
        if ($data['return_code'] == "SUCCESS") {
            $this->refundStatus($orderId); //修改订单状态为已退款

        } else {
            parent::error('退款失败', $data['return_msg']);
        }
    }

    //修改退款后的状态
    public function refundStatus($orderId)
    {
        $refund_number = 'tk' . (new Base())->makeOrderNo(); //退款单号
        return (new OrderModel())->where('id', '=', $orderId)->update([
            'tk_order' => 2,//已退款
            'tk_number' => $refund_number
        ]);

    }


    //核销记录
    public function generateCancel($order, $userId)
    {
        $user_info = (new UserModel())->where('id', '=', $order['user_id'])->find(); //当前用户信息
        (new CancelModel())->save([
            'user_id' => $order['user_id'], //核销用户
            'cancel_id' => $userId, //核销用户
            'product_id' => $order['product_id'], //商品ID
            'order_id' => $order['id'], //订单ID
            'order_number' => $order['order_number'], //订单编号
            'admission_time' => $order['admission_time'], //入场时间
            'appearance_time' => $order['appearance_time'], //出场时间
            'name' => $user_info['name'], //用户姓名
            'phone' => $user_info['phone'], //手机号
            'image' => $user_info['image'], //头像
        ]);
    }


    /**
     * 扫码核销
     * @param $userId
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @since: 2020/8/19
     * @author: Lin Zhengyu
     */
    public function cancellation($userId)
    {
        $request = Request::instance()->post();
        parent::validate($request, 'ApproachValidate');
        $cancel_id = (new UserModel())->where('id', '=', $userId)->value('cancel_id'); //核销员状态
        if ($request['code'] == 1) { // 如果是手动核销
            $qr_code = $request['qr_number'];
        } else {
            $qr_code = self::unlock_url($request['qr_number'], 'password'); //解密二维码里的核销码
        }
        $order = (new OrderModel())->where('qr_number', '=', $qr_code)->find(); //当前核销码所对应的订单
        if ($cancel_id != OrderEnum::CANCEL) {
            $this->error('该用户未被授权');
        }
        if ($qr_code != $order['qr_number']) {
            $this->error('该二维码或核销码不存在');
        }
        if ($order['status'] == OrderEnum::ORDER_WRITTEN) { //如果当前状态是待核销状态 则是入场
            $this->approach($order); //入场核销
        } else if ($order['status'] == OrderEnum::ORDER_SERVICE) { //如果当前状态是使用中，则是出场
            $this->appear($order, $qr_code, $userId); //出场核销
        }
    }


    //订单列表
    public function orderList($userId)
    {
        $status = Request::instance()->get('status', '');
        if ($status != '') {
            $where['status'] = $status;
        } else {
            $where['status'] = ['not in', [0]];
        }
        $where['user_id'] = $userId;
        $order = (new OrderModel())->where($where)->order('create_time desc')->select();

        $arr = array();
        foreach ($order as $value) {
            switch ($value['status']) {
                case OrderEnum::ORDER_WRITTEN: //待入场
                    array_push($arr, $this->putDataByStatusToOrder($value));
                    break;
                case OrderEnum::ORDER_SERVICE: //已入场
                    array_push($arr, $this->getOrderData($value));
                    break;
                case OrderEnum::ORDER_AFTER: //已出场
                    array_push($arr, $this->putDataByStatusToOrder($value));
                    break;
                case OrderEnum::ORDER_CANCEL: //已取消
                    array_push($arr, $this->getOrderData($value));
                    break;
            }
        }
        return $arr;

    }

    /**
     * @param $data
     * @return array
     * @author: Lin Zhengyu
     * @since: 2020/8/17
     */
    public function getOrderData($data)
    {
        $product_snapshot = json_decode($data['product_snapshot'], true);
        return [
            'order_id' => $data['id'],
            'name' => $product_snapshot['name'],  //名称
            'vessel_type' => $product_snapshot['vessel_type'], //类型
            'order_number' => $data['order_number'], //订单号
            'status' => $data['status'], //状态
            'price' => $data['price'], //最低消费金额
            'min_time' => $data['min_time'], //最低消费时间
            'order_price' => $data['order_price'], //押金
            'image' => $product_snapshot['image'], //图片
            'product_id' => $data['product_id'], //商品ID
            'is_reserve' => $data['is_reserve'], // yunzhihui 是否预约订单
            'reserve_date' => $data['reserve_date'], // yunzhihui 预约日期
        ];
    }

    /**
     *待入场，已出场订单处理
     * @param $data
     * @return array
     * @author: Lin Zhengyu
     * @since: 2020/8/17
     */
    public function putDataByStatusToOrder($data)
    {
        $res = $this->getOrderData($data);
        $res['tk_order'] = $data['tk_order']; //申请退款状态
        return $res;
    }


    /**
     * 二维码展示
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @author: Lin Zhengyu
     * @since: 2020/8/17
     */
    public function qrCode()
    {
        $request = Request::instance()->get();
        parent::validate($request, 'QrCodeValidate');
        $domain = Request::instance()->domain();
        $order = (new OrderModel())->where('id', '=', $request['order_id'])->find();
        $product_snapshot = json_decode($order['product_snapshot'], true);
        return [
            'qr_code' => $domain . $order['qr_code'], //二维码
            'qr_number' => $order['qr_number'], //核销编号
            'name' => $product_snapshot['name'], //名称
            'image' => $product_snapshot['image'], //图片
            'status' => $order['status'] //当前二维码状态
        ];
    }


    /**
     * 时间差（分）
     * @param $admission //开始时间
     * @param $appearance //结束时间
     * @return false|float
     * @since: 2020/8/15
     * @author: Lin Zhengyu
     */
    public function difference($admission, $appearance)
    {
        $d_time = ceil(($appearance - $admission) / 60);
        if ($d_time > 10) {
            $appearance_time = $appearance - OrderEnum::GIVE_TIME; //结束时间 - 赠送时间   赠送多十分钟给用户核销
        } else {
            $appearance_time = $appearance;
        }
        $admission_time = $admission; //开始时间
        return ceil(($appearance_time - $admission_time) / 60); //游玩时间
    }


    /**
     * 取消订单/申请退款
     * @param $userId
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @author: Lin Zhengyu
     * @since: 2020/8/17
     */
    public function deposit($userId)
    {
        $request = Request::instance()->post();
        parent::validate($request, 'TkOrderValidate');
        $where['id'] = $request['order_id'];
        $where['user_id'] = $userId;
        $order = (new OrderModel())->where($where)->find();
        if ($order['tk_order'] != OrderEnum::TK_REFUND) { //如果当前订单状态不是未退款
            $this->error('当前订单已退款或在退款中');
        }
        if ($order['status'] == OrderEnum::ORDER_WRITTEN) { //如果当前订单状态是待核销状态
            (new OrderModel())->where('id', '=', $order['id'])->update([
                'tk_price' => $order['order_price'],  //退款金额
            ]);
        }
        $data = (new OrderModel())->where('id', '=', $order['id'])->update([
            'tk_order' => OrderEnum::TK_PROGRESS,  //修改成退款中
            'apply_time' => time()  //申请退款时间
        ]);
        $this->success('提交成功,提交申请将在1-2工作日内处理', $data);
    }

    // yunzhihui 修改订单使用预约时间
    public function reserve($userId)
    {
        $request = Request::instance()->get();
        //parent::validate($request, 'TkOrderValidate');
        $where['id'] = $request['order_id'];
        $where['user_id'] = $userId;
        if(!isset($request['reserve_date'])){
            $data = (new OrderModel())->where($where)->update(['reserve_date' => $request['reserve_date']]);
        }
        return $data;
    }

    /**
     * 订单详情
     * @param $userId
     * @return array|false|\PDOStatement|string|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @since: 2020/8/17
     * @author: Lin Zhengyu
     */
    public function orderDetails($userId)
    {
        $request = Request::instance()->get();
        parent::validate($request, 'TkOrderValidate');
        $where['id'] = $request['order_id'];
        $where['user_id'] = $userId;
        $data = (new OrderModel())->where($where)->field('id,order_number,product_snapshot,price,min_time,after_price,after_min_time,order_price,tk_price,admission_time,appearance_time,diff_time')->find();
        if ($data['min_time'] >= 60) {
            $data['min_times'] = 1; //小时
        }
        $data['min_times'] = 0;
        $data['admission_time'] = date('Y-m-d H:i:s', $data['admission_time']);
        $data['appearance_time'] = date('Y-m-d H:i:s', $data['appearance_time']);
        $data['product_snapshot'] = json_decode($data['product_snapshot'], true);
        return $data;
    }

    //按人数核销
    public function apply($userId)
    {
        $request = Request::instance()->post();
        parent::validate($request, 'TkOrderValidate');
        $order = (new OrderModel())->where('id', '=', $request['order_id'])->find();
        if ($order['status'] != OrderEnum::ORDER_WRITTEN) { //当前不是待核销状态
            $this->error('订单错误');
        }
        if ($order['user_id'] == $userId) {
            (new OrderModel())->save([
                'status' => OrderEnum::ORDER_AFTER, //修改成已核销
                'update_time' => time()
            ], ['id' => $order['id']]);
        }
        $this->success('使用成功');
    }
}
